#include <my_head.h>

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd)
        ERR_LOG("socket error");
    
    // 填充服务器网络信息结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t serveraddr_len = sizeof(serveraddr);
    
    // 允许端口复用
    int flag = 1;
    if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)))
        ERR_LOG("setsockopt error");
    
    // 将套接字和服务器网络信息结构体绑定
    if(-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
        ERR_LOG("bind error");

    // 将套接字设置成被动监听状态
    if(-1 == listen(sockfd, 5))
        ERR_LOG("listen error");

    int max_fd = 0;
    // 创建集合
    fd_set readfds;
    FD_ZERO(&readfds);
    fd_set readfds_temp;
    FD_ZERO(&readfds_temp);
    // 将sockfd添加到集合中
    FD_SET(sockfd, &readfds);
    max_fd = max_fd > sockfd ? max_fd : sockfd; // 更新最大文件描述符

    int nbytes = 0;
    char buff[128] = {0};
    int acceptfd = 0;
    int ready_count = 0;
    struct timeval tm;
    while(1){
        // select会将tm结构体的两个成员减到0时 超时
        // 所以在循环中使用select时 需要每次重置超时时间
        tm.tv_sec = 5;
        tm.tv_usec = 0;

        readfds_temp = readfds;
        if(-1 == (ready_count = select(max_fd+1, &readfds_temp, NULL, NULL, &tm))){
            ERR_LOG("select error");
        }else if(0 == ready_count){
            // 在这里写超时后要做的事儿  具体做什么以实际项目要求为准
            printf("timeout..\n");
            continue;
        }
        // 判断哪些就绪了
        for(int i = 0; i < max_fd+1 && ready_count!=0; i++){
            if(FD_ISSET(i, &readfds_temp)){
                if(i == sockfd){ // 有新客户端连接了
                    if(-1 == (acceptfd = accept(i, NULL, NULL))){
                        ERR_LOG("accept error");
                    }
                    printf("客户端[%d]号连接到服务器..\n", acceptfd);
                    // 将新客户端的文件描述符添加到集合中
                    FD_SET(acceptfd, &readfds);
                    max_fd = max_fd > acceptfd ? max_fd : acceptfd; // 更新最大文件描述符
                }else{ // 已经连接的客户端发来消息了
                    // 接收客户端发来的数据
                    if(-1 == (nbytes = recv(i, buff, sizeof(buff), 0))){
                        ERR_LOG("recv error");
                    }else if(0 == nbytes){
                        printf("客户端[%d]号断开连接..\n", i);
                        // 将客户端对应的文件描述符在监视的集合中删除
                        FD_CLR(i, &readfds);
                        // 关闭文件描述符
                        close(i);
                        ready_count--;
                        continue;
                    }
                    if(!strcmp(buff, "quit")){
                        printf("客户端[%d]号退出了..\n", i);
                        // 将客户端对应的文件描述符在监视的集合中删除
                        FD_CLR(i, &readfds);
                        // 关闭文件描述符
                        close(i);
                        ready_count--;
                        continue;
                    }
                    // 处理数据（打印到终端）
                    printf("客户端[%d]号发来数据[%s]\n", i, buff);
                    // 组装应答消息
                    strcat(buff, "--beijing");
                    // 给客户端发送应答消息
                    if(-1 == send(i, buff, sizeof(buff), 0)){
                    ERR_LOG("send error");
                    }
                }
                ready_count--;
            }
        }
    }
    // 关闭套接字
    close(sockfd);

    return 0;
}